/*
@Time : 2018/11/9 下午6:57 
@Author : baoye
@File : proofofwork
@Software: GoLand
@brief: 
*/

package main

import (
	"math/big"
	"crypto/sha256"
	"bytes"
)

// 系统的难度值
const difficult = 20

// 定义proofofwork结构体
type ProofOfWork struct {
	// 数据来源
	block Block
	// 难度值
	target *big.Int
}

// 创建一个工作量证明
func NewProofOfWork(block Block) *ProofOfWork {

	// 创建一个结构体对象，并填充block
	pow := ProofOfWork{
		block:block,
	}

	// 根据系统难度生成target
	target := big.NewInt(1)
	target.Lsh(target, 256 - difficult)
	pow.target = target

	return &pow
}

// 执行Run，生成一个满足条件的nounce值和hash值
func (pow *ProofOfWork)Run()([]byte, uint64)  {

	var nonce = uint64(0)
	for {
		// 获取info
		info := pow.PrepareInfo(nonce)
		// 对info做hash运算
		hash := sha256.Sum256(info)

		// 判断是否满足
		var hashInt big.Int
		hashInt.SetBytes(hash[:])
		if hashInt.Cmp(pow.target) == -1 {
			return hash[:], nonce
		}else {
			nonce++
		}
	}
}

func (pow *ProofOfWork)PrepareInfo(nonce uint64) []byte {
	block := pow.block
	bytesArray := [][]byte {
		Uint2Byte(block.Version),
		block.PrevBlockHash,
		block.MerkelRoot,
		Uint2Byte(block.TimeStamp),
		Uint2Byte(block.Difficulty),
		Uint2Byte(nonce),
		//block.Data,
	}
	info := bytes.Join(bytesArray,[]byte{})
	return info

}

// 校验函数
func (pow *ProofOfWork)IsValid() bool {
	info := pow.PrepareInfo(pow.block.Nonce)
	hash := sha256.Sum256(info)

	var hashInt big.Int
	hashInt.SetBytes(hash[:])

	return hashInt.Cmp(pow.target) == -1
}

